package com.qnkf.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import com.qnkf.code.RedisCode;
import com.qnkf.model.ResultModel;
import com.qnkf.service.BaseService;
import com.qnkf.service.HomeService;
import com.qnkf.util.*;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Dercsription 登录
 * @Author Administrator
 * @CreateDate 2021/8/28 13:59
 **/
@Service
@Transactional
@Slf4j
public class HomeServiceImpl extends BaseService implements HomeService {

    /**
     * 微信登录
     * @param request
     * @return
     */
    @Override
    public ResultModel loginWechat(JSONObject request) {
        log.info(" 微信登录request:{} ",request);
        if (null == request || StringUtils.isEmpty(request.getString("code"))){
            return ResultModel.fail("入参错误~");
        }
        //前端后期的code
        String code = request.getString("code");
        //用户敏感信息
        String encrypteData = request.getString("encrypteData");
        //解密算法的向量
        String iv = request.getString("iv");

        //调用微信接口获取用户数据
        JSONObject weixiInfo = this.getSessionKeyAndOpenId(code);
        log.info("微信登陆返回内容：" + weixiInfo);
        String openId = weixiInfo.getString("openid");
        String sessionKey = weixiInfo.getString("session_key");
        String errCode = weixiInfo.getString("errcode");
        if (!org.springframework.util.StringUtils.isEmpty(errCode)) {
            return ResultModel.fail("登陆Code已过期");
        }
        if (org.springframework.util.StringUtils.isEmpty(openId)) {
            return ResultModel.fail("获取微信用户信息失败");
        }

        //如果前端请求了微信接口从中获取了用户信息
        JSONObject weixiInfos = new JSONObject();
        weixiInfos = getUserInfo(encrypteData,sessionKey,iv);
        log.info("用户session信息:" + weixiInfos);
        String unionid = weixiInfo.getString("unionid");
        if (StringUtils.isEmpty(unionid)){
            unionid = weixiInfos.getString("unionid");
        }

        //查询是否存在改用户
        JSONObject userInfo = userMapper.getUserInfo(new JSONObject().fluentPut("unionid",unionid));
        if (null == userInfo){
            //不存在进行数据入库
            JSONObject userModel = new JSONObject();
            userModel.put("userId",StringUtils.getGUID());
            userModel.put("userLogo", weixiInfo.getString("avatarUrl"));
            userModel.put("openId", weixiInfo.getString("openId"));
            userModel.put("nickeName", weixiInfo.getString("nickeName"));
            userModel.put("gender", weixiInfo.getString("gender"));
            userModel.put("avatarUrl", weixiInfo.getString("avatarUrl"));
            userModel.put("unionid", unionid);
            userModel.put("city", weixiInfo.getString("city"));
            userModel.put("province", weixiInfo.getString("province"));
            userModel.put("country", weixiInfo.getString("country"));
            userModel.put("createTime", DateUtil.getDateString());
            log.info("新用户 userInfo:{}",userInfo);
            Boolean b = userMapper.addUser(userModel);
            if (!b){
                throw new RuntimeException("新用户入驻异常");
            }
            //生成token
            userModel.put("token",createTokens(userModel));
            return ResultModel.success(userModel);
        } else {
            //已入驻用户
            log.info("已入驻用户 userInfo:{}",userInfo);
            userInfo.put("token",createTokens(userInfo));
            return ResultModel.success(userInfo);
        }
    }

    /**
     * 获取微信小程序的session_key和openid
     *
     * @param code 微信前端login()方法返回的code
     * @return jsonObject
     * @author hengyang4
     */
    public JSONObject getSessionKeyAndOpenId(String code) {
        //微信登录的code值
        String wxCode = code;
        //读取属性文件
        //服务器端调用接口的url
        String requestUrl = wechatConfig.getWxLogin();
        String appId = wechatConfig.getAppId();
        String secret = wechatConfig.getAppSecret();
        //封装需要的参数信息
        Map<String, String> requestUrlParam = new HashMap<>(4);
        //开发者设置中的appId
        requestUrlParam.put("appid", appId);
        //开发者设置中的appSecret
        requestUrlParam.put("secret", secret);
        //小程序调用wx.login返回的code
        requestUrlParam.put("js_code", wxCode);
        //默认参数
        requestUrlParam.put("grant_type", "authorization_code");
        JSONObject jsonObject = JSON.parseObject(HttpUtil.sendPost(requestUrl, requestUrlParam));
        return jsonObject;
    }

    /**
     * 解密用户敏感数据获取用户信息
     *
     * @param sessionKey    数据进行加密签名的密钥
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     * @param iv            加密算法的初始向量
     * @return
     * @author zhy
     */
    public JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {
        String result = "";
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            // 初始化
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                result = new String(resultByte, "UTF-8");
            }
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
        } catch (NoSuchPaddingException e) {
            log.error(e.getMessage(), e);
        } catch (InvalidParameterSpecException e) {
            log.error(e.getMessage(), e);
        } catch (IllegalBlockSizeException e) {
            log.error(e.getMessage(), e);
        } catch (BadPaddingException e) {
            log.error(e.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        } catch (InvalidAlgorithmParameterException e) {
            log.error(e.getMessage(), e);
        } catch (NoSuchProviderException e) {
            log.error(e.getMessage(), e);
        } catch (java.security.InvalidKeyException e) {
            log.error(e.getMessage(), e);
        }
        return JSON.parseObject(result);
    }

    /**
     * 手机号登录
     * @param request
     * @return
     */
    @Override
    public ResultModel loginPhone(JSONObject request) {
        log.info(" 手机号登录request:{} ",request);
        JSONObject jsonObject = userMapper.getUserInfo(request);
        jsonObject.put("token",createTokens(jsonObject));
        return ResultModel.success(jsonObject);
    }

    /**
     * 生成token并保存到redis记录有效时间
     */
    public String createTokens(JSONObject model){
        try {
            String token = JwtUtil.createToken(model);
            JSONObject josn = JwtUtil.verifyToken(token);
            JSONObject json2 = new JSONObject();
            json2.put("expiration", josn.getString("expirationToken"));
            json2.put("token", token);
            //将token的过期时间存在redis
            redisUtil.set(RedisCode.getUserToken(josn.getString("userId")),json2,
                    josn.getLongValue("tokenTime")  / 500);

            return token;
        } catch (Exception e) {
            e.printStackTrace();
            log.info("  :{}",e.getMessage());
            return null;
        }
    }
}
